package com.bf.utils;

import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Function;

@Getter
@Slf4j
public class BfThreadPoolReturnUtils<T> extends BfThreadPoolCommon {
    //返回值不处理子任务(子任务有方法名)
    protected CopyOnWriteArrayList<Map<String, MyFunction<T>>> functionsName = new CopyOnWriteArrayList<>();
    //返回值处理方法（仅对于有返回值的子任务）
    private Function<List<T>, List<T>> resultFunction;
    // 有返回值方法的 返回值
    private List<T> object;

    public BfThreadPoolReturnUtils(String jobName) {
        this.jobName = jobName;
    }
    public BfThreadPoolReturnUtils() {
    }

    /**
     * 执行有返回方法
     *
     * @return
     */
    @SuppressWarnings("CallToPrintStackTrace")
    public List<T> run() {
        initialize();
        // 返回结果
        List<T> resultList = new ArrayList<>();
        List<MyFutureTask<T>> futureTaskList = new ArrayList<>();
        for (int i = 0; i < functionsName.size(); i++) {
            int finalI = i;
            MyFunction<T> myFunction = functionsName.get(i).values().iterator().next();
            // 执行方法
            FutureTask<T> futureTask = new FutureTask<T>(() -> {
                Function<Integer, T> integerTFunction = myFunction.getFunction();
                try {
                    return integerTFunction.apply(finalI);
                }catch (Exception e){
                    log.error("子任务{}执行异常：{}", functionsName.get(finalI).keySet().iterator().next(), e.getMessage());
                    throw e;
                }
            });
            futureTaskList.add(new MyFutureTask<T>(futureTask, myFunction.getTimeout(), myFunction.getUnit()));
            threadPoolExecutor.submit(futureTask);
        }

        // 获取结果
        for (MyFutureTask<T> myFutureTask : futureTaskList) {
            try {
                FutureTask<T> futureTask = myFutureTask.getFutureTask();
                // get方法会阻塞，直到获取结果为止
                // futureTask.get();
                resultList.add(futureTask.get(myFutureTask.getTimeout(), myFutureTask.getUnit()));
            } catch (InterruptedException e) { // 线程被中断将会进入此处
                //noinspection CallToPrintStackTrace
                e.printStackTrace();
                log.info("-->中断异常");
                throw new RuntimeException("系统异常!");
            } catch (ExecutionException e) { // 线程执行异常将会进入此处
                e.printStackTrace();
                log.info("-->执行异常");
                throw new RuntimeException("系统异常!");
            } catch (TimeoutException e) { // 获取结果超时将会进入此处
                e.printStackTrace();
                log.info("-->超时异常");
                throw new RuntimeException("系统异常!");
            }
        }

        return resultList;
    }

    /**
     * 添加有返回值任务，所有任务执行完成后进行处理的方法
     *
     * @param resultFunction
     * @return
     */
    public BfThreadPoolReturnUtils<T> addResultFunctions(Function<List<T>, List<T>> resultFunction) {
        this.resultFunction = resultFunction;
        return this;
    }

    /**
     * ‘添加有返回值任务
     *
     * @param function
     * @param name
     * @return
     */
    public BfThreadPoolReturnUtils<T> addFunctions(long timeout, TimeUnit unit, Function<Integer, T> function, String name) {
        functionsName.add(new HashMap<String, MyFunction<T>>() {{
            put(name, new MyFunction<T>(function, timeout, unit));
        }});
        return this;
    }

    public BfThreadPoolReturnUtils<T> addFunctions(Function<Integer, T> function, String name) {
        functionsName.add(new HashMap<String, MyFunction<T>>() {{
            put(name, new MyFunction<T>(function));
        }});
        return this;
    }


    @Data
    public static class MyFutureTask<T> {
        FutureTask<T> futureTask;
        long timeout;
        TimeUnit unit;

        public MyFutureTask() {
        }

        public MyFutureTask(FutureTask<T> futureTask, long timeout, TimeUnit unit) {
            this.futureTask = futureTask;
            this.timeout = timeout;
            this.unit = unit;
        }
    }

    @Data
    public static class MyFunction<T> {
        Function<Integer, T> function;
        long timeout = 120L;
        TimeUnit unit = TimeUnit.SECONDS;

        public MyFunction() {
        }

        public MyFunction(Function<Integer, T> function, long timeout, TimeUnit unit) {
            this.function = function;
            this.timeout = timeout;
            this.unit = unit;
        }

        public MyFunction(Function<Integer, T> function) {
            this.function = function;
        }
    }
}
